/******************************************************************************
 * Copyright 2020-2021 The Firmament Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include <board.h>
#include <firmament.h>
#include "hal/actuator/actuator.h"

#ifdef FMT_USING_CM_BACKTRACE
    #include <cm_backtrace.h>
#endif

#include "module/task_manager/task_manager.h"

void assert_failed(uint8_t* file, uint32_t line)
{
    rt_hw_interrupt_disable();

#ifdef FMT_USING_CM_BACKTRACE
    cm_backtrace_assert(cmb_get_sp());
#endif

    while (1)
        ;
}

static void assert_hook(const char* ex, const char* func, rt_size_t line)
{
    printf("(%s) assertion failed at function:%s, line number:%ld \n", ex, func, line);

#ifdef FMT_USING_CHECKED
    assert_failed((uint8_t*)func, (uint32_t)line);
#endif
}

/*---------------------------------------------------------------*/
/* 用于接收消息的信号量 */
static struct rt_semaphore rx_sem;
static rt_device_t serial_dev;

/* Rx 接收回调函数 */
rt_err_t uart_rx_ind_callback(rt_device_t dev, rt_size_t size)
{
    /* 串口接收到数据后产生中断，调用此回调函数，然后发送接收信号量, 唤醒接收线程 */
    rt_sem_release(&rx_sem);

    return RT_EOK;
}

static void rt_thread_uart_test(void* parameter)
{
    char ch;
    rt_err_t ret;

    rt_device_t serial_dev = rt_device_find("serial0");
    if (serial_dev == RT_NULL) {
        rt_kprintf("utest: serial device not found\n");
        return;
    }

    rt_sem_init(&rx_sem, "uart_rx_sem", 0, RT_IPC_FLAG_PRIO);

    /* 以中断接收及轮询发送模式打开串口设备 */
    ret = rt_device_open(serial_dev, RT_DEVICE_FLAG_INT_RX);
    if (ret != RT_EOK) {
        rt_kprintf("utest: open serial device failed, ret = %d\n", ret);
        return;
    }

    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(serial_dev, uart_rx_ind_callback);


    while (1) {
        /* 从串口读取 1 个字节的数据, 如果没有读取到数据则阻塞等待接收信号量 */
        // while(1 != rt_device_read(serial_dev, 0, &ch, 1)) {

        //     /* 阻塞等待接收信号量, 等待当前线程被唤醒后, 再次读取数据 */
        //     rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
        // }

        /* 读取接收到的 1个字节数据, pirntf输出 */
        //rt_kprintf("utest: received char = 0x%x\n", ch);

        ch = 'h';

        /* 读取到的数据, 再通过串口输出出去 */
        rt_device_write(serial_dev, 0, &ch, 1);
    }
}

/*----------------------------------------------------------*/
static void rt_thread_pwm_test(void* parameter)
{
    uint16_t duty_val = 1500; // Example PWM value

    rt_device_t pwm_dev = rt_device_find("main_out");
    actuator_dev_t act_dev = (struct actuator_device*)pwm_dev;

    if (act_dev == RT_NULL) {
        rt_kprintf("utest: PWM device not found\n");
        return;
    }

    if (rt_device_open(pwm_dev, RT_DEVICE_FLAG_RDWR) != RT_EOK) {
        rt_kprintf("utest: open PWM device failed\n");
        return;
    }

    while (1) {
        rt_device_write(pwm_dev, 1, &duty_val, 1);

        /* read pwm channel 0 value */
        rt_uint16_t chan_val_read[16] = { 0 };
        rt_device_read(pwm_dev, 1, chan_val_read, 1);

        rt_thread_mdelay(1000);
    }
}

/*----------------------------------------------------------*/
static void rt_thread_adc_test(void* parameter)
{
    uint32_t value;
    uint16_t battery_voltage;

    rt_device_t adc_dev = rt_device_find("adc0");
    if (adc_dev == RT_NULL) {
        rt_kprintf("utest: ADC device not found\n");
        return;
    }

    if (rt_device_open(adc_dev, RT_DEVICE_FLAG_RDONLY) != RT_EOK) {
        rt_kprintf("utest: open ADC device failed\n");
        return;
    }

    while (1) {

        if (rt_device_read(adc_dev, 0, &value, sizeof(value)) != sizeof(value)) {
            rt_kprintf("utest: read ADC device failed\n");
        }

        rt_kprintf("thread adc value: 0x%x %d \r\n", value, value);
        rt_thread_mdelay(1000);
    }
}

static int utest_init(void)
{
    rt_err_t ret;
    rt_thread_t tid0, tid1, tid2;

    /*------------------------------------------*/
    tid0 = rt_thread_create("uart_test",
                            rt_thread_uart_test,
                            RT_NULL,
                            1024,
                            20,
                            10);

    if (tid0 != RT_NULL) {
        rt_thread_startup(tid0);
        ret = RT_EOK;
    } else {
        rt_kprintf("utest: create UART test thread failed\n");
        ret = RT_ERROR;
    }

#if 0
    /*------------------------------------------*/
    tid1 = rt_thread_create("pwm_test",
                            rt_thread_pwm_test,
                            RT_NULL,
                            1024,
                            19,
                            10);

    if (tid1 != RT_NULL) {
        rt_thread_startup(tid1);
        ret = RT_EOK;
    } else {
        rt_kprintf("utest: create PWM test thread failed\n");
        ret = RT_ERROR;
    }

    /*------------------------------------------*/
    tid2 = rt_thread_create("adc_test",
                            rt_thread_adc_test,
                            RT_NULL,
                            1024,
                            19,
                            10);

    if (tid2 != RT_NULL) {
        rt_thread_startup(tid2);
        ret = RT_EOK;
    } else {
        rt_kprintf("utest: create ADC test thread failed\n");
        ret = RT_ERROR;
    }
#endif

    return ret;
}

int fmt_main(void)
{
    /* 单元测试 */
  //  utest_init();
   // return 0;

#if defined(RT_USING_CPLUSPLUS)
    /* c++ component initialization */
    int cplusplus_system_init(void);
    cplusplus_system_init();
#endif

    /* bsp initialization */
    bsp_initialize();

    /* task initialization */
    task_manager_init();

    /* bsp post initialization */
    bsp_post_initialize();

    /* start task */
    task_manager_start();

    return 0;
}
